#include "NextAlgo.h"


NextAlgo::NextAlgo(SystemView* sys, int sortType, Writer* w, Parser* prs, double constantG, int noTimeSlices) : Algorithm(sys,sortType,w, prs)
{	
	L = noTimeSlices;
	if (L > 1)// Gconstant is iralivent if L == 1
		G = constantG;
	else
		G = 1;
	GD=new GradientDescent(sys);
}

NextAlgo::~NextAlgo(void)
{
	delete ssdExtents;
	delete fcExtents;
	delete sataExtents;
	map<string, ExtentStatsAlgo*>::iterator iter;
    for (iter=sortedExtents->begin(); iter!=sortedExtents->end(); iter++){
        (*iter).second->deleteContent();
        delete (*iter).second;
    }
	delete GD;
}

void NextAlgo::runAlgo(int iteration){

	all_damages->clear();

	double ssdSpace = sys->getAllocation()->getSSD_size() / sys->getAllocation()->getExtent_size(); //num of extents that can be in SSD according to it's space- 300GB (Extent = 10MB)
	double fcSpace = sys->getAllocation()->getFC_size() / sys->getAllocation()->getExtent_size();
	double sataSpace = sys->getAllocation()->getSATA_size() / sys->getAllocation()->getExtent_size();

	vector<ExtentEntry*>::iterator extentsIt;
	vector<ExtentEntry*>* extents;
	vector<ExtentStatsAlgo*>* temp;

	int timeToDelete=0;
	int timeSliceCount=1;
	int oldPlace;

	while ((extents = parser->ParseNextTimeSlice()) != 0){
		int time = extents->front()->getMy_time();

		if (L < timeSliceCount){
			timeToDelete = time - sys->getTimeSliceSize()*L;
		}
		else {
			timeToDelete=0;
		}
		
		createSortedExtents(extents, timeToDelete, time);

		ExtentStatsAlgo* ex;
		vector<ExtentStatsAlgo*>::iterator sortIter;
		if (timeSliceCount > 1){
			// updating util for current time slice before calculating reponse time
			for(sortIter = sortedAlgo->begin(); sortIter != sortedAlgo->end(); sortIter++){
				ex=(*sortIter);
				oldPlace = findOldPlace(ex);
				if (oldPlace==0){
					sataExtents->push_back(ex);
					sata->encUtilization(time, ex);
				}
				else if (oldPlace==1){
					ssd->encUtilization(time, ex);
				}
				else if (oldPlace==2){
					fc->encUtilization(time, ex);
				}
				else if (oldPlace==3){
					sata->encUtilization(time, ex);
				}
			}
			// calling function to calculate each lun's response time for this time slice
			timeSliceCalc->responseTime(ssdExtents, fcExtents, sataExtents, time, ssd, fc, sata, 0, 0, 0, 0);
			(*all_damages)[time] = timeSliceCalc->sum_damage(time);
		}

		ssdExtents->clear();
		fcExtents->clear();
		sataExtents->clear();
		ssd->clearUtilData();
		fc->clearUtilData();
		sata->clearUtilData();

		if (iteration==3){
			writer->writeGroupRT(time);
			if (timeSliceCount > 1){
				sortedAlgo=GD->Run(sortedAlgo, timeSliceCount, G, ssdSpace, fcSpace, sataSpace, time);
			}
			writer->writeAllLunPriority(time);
		}

		temp = sorter->sort(sortedAlgo);
		delete sortedAlgo;
		sortedAlgo = temp;
		
		// checks for each group, whats the highest and lowest extents when sorted - helps to predict the results - how mixed the groups are
		if (DEBUG>1){
			ofstream* log = (writer->logFile());
			if (!(*log).is_open()){
				(*log).open("../DataBase/OutputFiles/logFile.txt", ofstream::app);
			}
			(*log)<<"1 high: "<< sorter->high1<<" 1 low: " <<sorter->low1<<endl;
			(*log)<<"2 high: "<< sorter->high2<<" 2 low: " <<sorter->low2<<endl;
			(*log)<<"3 high: "<< sorter->high3<<" 3 low: " <<sorter->low3<<endl;
			(*log).close();
		}

		
		// placing extents in the devices to prepare for next time slice
		vector<ExtentStatsAlgo*>::iterator sortIt;
		for(sortIt = sortedAlgo->begin(); sortIt != sortedAlgo->end(); sortIt++){
			if((ssdExtents->size() < ssdSpace) && ssd->checkUtilization(time,(*sortIt))){
				ssd->encUtilizationNoSave(time, *sortIt);
				ssdExtents->push_back(*sortIt);
				(*sortIt)->getExtent()->setPlace(1);
			}
			else if((fcExtents->size() < fcSpace) && fc->checkUtilization(time,(*sortIt))){
				fc->encUtilizationNoSave(time, *sortIt);
				fcExtents->push_back(*sortIt);
				(*sortIt)->getExtent()->setPlace(2);
			}
			else {
				if (DEBUG>0){
					if (sataExtents->size() > sataSpace)
						writer->writeLog("SATA space is over!!!!!");


					if (!sata->checkUtilization(time,*sortIt) )
						writer->writeLog("SATA util not pass!!!");
				}
				sata->encUtilizationNoSave(time, *sortIt);
				sataExtents->push_back(*sortIt);
				(*sortIt)->getExtent()->setPlace(3);
			}
		}

		/*******************************************************New function call to write extent placements every time slice************************************************************/

		if (iteration==1){
			WritePlacementsNoPriority(ssdExtents, fcExtents, sataExtents, time);
		}
		
		if (iteration==3){
			WritePlacements(ssdExtents, fcExtents, sataExtents, time);
		}
		/********************************************************************************************************************************************************************************/


		//calc Division of groups percents	
		ssdDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,1,time)));
		fcDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,1,time)));
		sataDivisionPercGroup1->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,1,time)));

		ssdDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,2,time)));
		fcDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,2,time)));
		sataDivisionPercGroup2->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,2,time)));

		ssdDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(ssdExtents,3,time)));
		fcDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(fcExtents,3,time)));
		sataDivisionPercGroup3->insert(pair<int,double>(time,divisionOfPriorityGroup(sataExtents,3,time)));

		// delete extentStatistics in all extentStatsAlgo
		for(sortIt = sortedAlgo->begin(); sortIt != sortedAlgo->end(); sortIt++){	
			if ((*sortIt)->getStats()->size()!=0){
				(*sortIt)->deleteStat();
			}
		}
		
		if (timeSliceCount>1)
			cout << "time: " << time << " damage: " << all_damages->at(time) << endl;

		timeSliceCount++;
	
		ssd->clearUtilData();
		fc->clearUtilData();
		sata->clearUtilData();
		sortedAlgo->clear();
	}

	delete extents;

	//******************** writing damage to files for graphs **************************************//
	string algoName="Next";
	string methodName="";
	switch(iteration){
		case 1:
			methodName="Priority=1";
			break;
		case 2:
			methodName="Priority=1RT";
			break;
		case 3:
			methodName="gradientDescent";
			break;
	}
	writer->writeAlgoDamage(iteration, all_damages, algoName, methodName);
	writer->writeMethodDamage(iteration, all_damages, algoName, methodName);
	writer->writeDivisionOfPriorityGroups(iteration,1,ssdDivisionPercGroup1,fcDivisionPercGroup1,sataDivisionPercGroup1);
	writer->writeDivisionOfPriorityGroups(iteration,2,ssdDivisionPercGroup2,fcDivisionPercGroup2,sataDivisionPercGroup2);
	writer->writeDivisionOfPriorityGroups(iteration,3,ssdDivisionPercGroup3,fcDivisionPercGroup3,sataDivisionPercGroup3);
	//************************************************************************************************//
}

double NextAlgo::divisionOfPriorityGroup(vector<ExtentStatsAlgo*>* l, int groupNo, int time){
	int num = 0;
	vector<ExtentStatsAlgo*>::iterator itl;
	for (itl = l->begin(); itl != l->end(); itl++){
		ExtentStatsAlgo* ex = *itl;
		if (ex->getExtent()->getLun()->getPriorityGroup() == groupNo && (ex->getStat(time)!=0))// && (ex->getStat(time)->getStat(rand_rm)>0))
			num++;
	}
	if (l->size() == 0)
		return 0;
	else
		return ((double)num)/((double)l->size());
}

int NextAlgo::findOldPlace(ExtentStatsAlgo* ex){
	int place=ex->getExtent()->getPlace();
	ex->getExtent()->setPlace(0);
	return place;
}

void NextAlgo::createSortedExtents(vector<ExtentEntry*>* list, int timeToDelete, int currentTime){
	vector<ExtentEntry*>::iterator itList;
	int count=0;
	int size = list->size();
	//if extent already exist in sortedExtents just add statistics else insert new ExtentStatAlgo
	for(itList = list->begin(); itList != list->end(); itList++){
		if (!alreadyExistsInSortedExtents(*itList)){
			addToSorted(itList);
		}
		delete *itList;
	}

	/*old horizon**************************************************
	// deleting time statistics that arent relevent anymore
	map<string, ExtentStatsAlgo*>::iterator iter;
	for (iter=sortedExtents->begin(); iter!=sortedExtents->end();){
		if ((*iter).second->getStat(timeToDelete)!=0){
			(*iter).second->removeStatsOfTime(timeToDelete);
			if ((*iter).second->getStatSize() == 0){
				(*iter).second->deleteContent();
				delete (*iter).second;
				iter = sortedExtents->erase(iter);
				//if (iter!=sortedExtents->begin())
				//	iter--;
			}
			else{
				(*iter).second->multiplyG(G, currentTime);
				sortedAlgo->push_back((*iter).second);
				iter++;
			}
		}
		else{
			(*iter).second->multiplyG(G, currentTime);
			sortedAlgo->push_back((*iter).second);
			iter++;
		}
	}
	*/
	map<string, ExtentStatsAlgo*>::iterator iter;
	for (iter=sortedExtents->begin(); iter!=sortedExtents->end();iter++){
		sortedAlgo->push_back((*iter).second);
	}

	delete list;
}

bool NextAlgo::alreadyExistsInSortedExtents(ExtentEntry* ex){
	map<string, ExtentStatsAlgo*>::iterator itSorted;
	char buffer[30];
	string key = "";
	_itoa_s(ex->getMy_extent()->getLun()->getM_uid(),buffer,30, 10);
	key.append(buffer);
	key.append("_");
	sprintf(buffer, "%I64d", ex->getMy_extent()->my_offset);
	//_itoa_s(ex->getMy_extent()->my_offset, buffer,30, 10);
	key.append(buffer);
	itSorted=sortedExtents->find(key);
	if (itSorted!=sortedExtents->end()){
		/*old horizon code
		(*itSorted).second->addStat(ex->getMy_stat());
		*/
		(*itSorted).second->addStat(ex->getMy_stat());
		(*itSorted).second->multiplyGInfinite(G);
		(*itSorted).second->addTotalRM(ex->getMy_stat()->getStat(rand_rm));
		return true;
	}
	return false;
}

void NextAlgo::addToSorted(vector<ExtentEntry*>::iterator itList){
	char buffer[30];
	string key = "";
	_itoa_s((*itList)->getMy_extent()->getLun()->getM_uid(),buffer,30, 10);
	key.append(buffer);
	key.append("_");
	sprintf(buffer, "%I64d", (*itList)->getMy_extent()->my_offset);
	//_itoa_s((*itList)->getMy_extent()->my_offset, buffer,30, 10);
	key.append(buffer);
	ExtentStatsAlgo* ex = new ExtentStatsAlgo((*itList)->getMy_extent());
	/*old horizon code
	ex->addStat((*itList)->getMy_stat());
	*/
	ex->addStat((*itList)->getMy_stat());
	ex->setTotalRM((*itList)->getMy_stat()->getStat(rand_rm));
	sortedExtents->insert(pair<string, ExtentStatsAlgo*> (key, ex));
}
